π Load required
libraries
library(ggplot2)
library(htmlwidgets)
library(igraph)
library(monocle3)
library(plotly)
set.seed(12345)
𧬠Constructing
single-cell trajectories
During development, in response to stimuli, and throughout life,
cells transition from one functional βstateβ to another. Cells in
different states express different sets of genes, producing a dynamic
repetoire of proteins and metabolites that carry out their work. As
cells move between states, they undergo a process of transcriptional
re-configuration, with some genes being silenced and others newly
activated. These transient states are often hard to characterize because
purifying cells in between more stable endpoint states can be difficult
or impossible. Single-cell RNA-Seq can enable you to see these states
without the need for purification. However, to do so, we must determine
where each cell is in the range of possible states. For a comparison of
different trajectory inference methods, see Saelens et al.Β (2019) (Saelens et al. 2019).
Monocle 3 (Cao et al. 2019) is the
latest version of the Monocle toolkit. Monocle introduced the strategy
of using RNA-Seq for single-cell trajectory analysis (Trapnell et al. 2014). Rather than purifying
cells into discrete states experimentally, Monocle uses an algorithm to
learn the sequence of gene expression changes each cell must go through
as part of a dynamic biological process. Once it has learned the overall
βtrajectoryβ of gene expression changes, Monocle can place each cell at
its proper position in the trajectory. You can then use Monocleβs
differential analysis toolkit to find genes regulated over the course of
the trajectory, as described in the section Finding genes that
change as a function of pseudotime. If there are multiple outcomes
for the process, Monocle will reconstruct a βbranchedβ trajectory. These
branches correspond to cellular βdecisionsβ, and Monocle provides
powerful tools for identifying the genes affected by them and involved
in making them. You can see how to analyze branches in the section
Analyzing branches in single-cell trajectories.
The workflow for reconstructing trajectories is very similar to the
workflow for clustering, but it has a few additional steps. To
illustrate the workflow, we will use another C. elegans data
set, this one from Packer & Zhu et al. (Packer et al. 2019). Their study includes a
time series analysis of whole developing embyros. We will examine a
small subset of the data which includes most of the neurons.
π₯ Loading the
data
We will load the data as we did with the L2 data. This dataset
contains an expression matrix, cell metadata, and gene annotations.
expression_matrix <- readRDS(url("https://depts.washington.edu:/trapnell-lab/software/monocle3/celegans/data/packer_embryo_expression.rds"))
cell_metadata <- readRDS(url("https://depts.washington.edu:/trapnell-lab/software/monocle3/celegans/data/packer_embryo_colData.rds"))
gene_annotation <- readRDS(url("https://depts.washington.edu:/trapnell-lab/software/monocle3/celegans/data/packer_embryo_rowData.rds"))
cds <- new_cell_data_set(expression_matrix,
cell_metadata = cell_metadata,
gene_metadata = gene_annotation)
βοΈ Pre-process the
data
Pre-processing works exactly as in clustering analysis. This time, we
will use a different strategy for batch correction, which includes what
Packer & Zhu et al did in their original analysis.
Note: Your data will not have the loading batch
information demonstrated here, you will correct batch using your own
batch information.
cds <- preprocess_cds(cds, num_dim = 50)
cds <- align_cds(cds, alignment_group = "batch", residual_model_formula_str = "~ bg.300.loading + bg.400.loading + bg.500.1.loading + bg.500.2.loading + bg.r17.loading + bg.b01.loading + bg.b02.loading")
Note that in addition to using the alignment_group
argument to align_cds(), which aligns groups of cells
(i.e.Β batches), we are also using
residual_model_formula_str. This argument is for
subtracting continuous effects. You can use this to control for things
like the fraction of mitochondrial reads in each cell, which is
sometimes used as a QC metric for each cell. In this experiment (as in
many scRNA-seq experiments), some cells spontanously lyse, releasing
their mRNAs into the cell suspension immediately prior to loading into
the single-cell library prep. This βsupernatant RNAβ contaminates each
cellsβ transcriptome profile to a certain extent. Fortunately, it is
fairly straightforward to estimate the level of background contamination
in each batch of cells and subtract it, which is what Packer et al did
in the original study. Each of the columns bg.300.loading,
bg.400.loading, corresponds to a background signal that a
cell might be contaminated with. Passing these colums as terms in the
residual_model_formula_str tells align_cds()
to subtract these signals prior to dimensionality reduction, clustering,
and trajectory inference. Note that you can call
align_cds() with alignment_group,
residual_model_formula, or both.
π¨ Reduce
dimensionality and visualize the results
Next, we reduce the dimensionality of the data. However, unlike
clustering, which works well with both UMAP and t-SNE, here we strongly
urge you to use UMAP, the default method:
cds <- reduce_dimension(cds)
p1 <- plot_cells(cds, label_groups_by_cluster=FALSE, color_cells_by = "cell.type")
ggsave("figures/monocle3_trajectory_umap.pdf", p1, width=12, height=10)
ggsave("figures/monocle3_trajectory_umap.jpg", p1, width=12, height=10)
Figure 1: UMAP of C. elegans embryonic cells colored by
cell type
As you can see, despite the fact that we are only looking at a small
slice of this dataset, Monocle reconstructs a trajectory with numerous
branches. Overlaying the manual annotations on the UMAP reveals that
these branches are principally occupied by one cell type.
As with clustering analysis, you can use plot_cells() to
visualize how individual genes vary along the trajectory. Letβs look at
some genes with interesting patterns of expression in ciliated
neurons:
ciliated_genes <- c("che-1",
"hlh-17",
"nhr-6",
"dmd-6",
"ceh-36",
"ham-1")
p2 <- plot_cells(cds,
genes=ciliated_genes,
label_cell_groups=FALSE,
show_trajectory_graph=FALSE)
ggsave("figures/monocle3_trajectory_ciliated_genes.pdf", p2, width=12, height=10)
ggsave("figures/monocle3_trajectory_ciliated_genes.jpg", p2, width=12, height=10)
Figure 2: Expression of ciliated neuron genes along the
trajectory
We will learn how to identify the genes that are restricted to each
outcome of the trajectory later on in the section Finding genes that
change as a function of pseudotime.
π¬ Cluster your
cells
Although cells may continuously transition from one state to the next
with no discrete boundary between them, Monocle does not assume that all
cells in the dataset descend from a common transcriptional βancestorβ.
In many experiments, there might in fact be multiple distinct
trajectories. For example, in a tissue responding to an infection,
tissue resident immune cells and stromal cells will have very different
initial transcriptomes, and will respond to infection quite differently,
so they should be a part of the same trajectory.
Monocle is able to learn when cells should be placed in the same
trajectory as opposed to separate trajectories through its clustering
procedure. Recall that we run cluster_cells(), each cell is
assigned not only to a cluster but also to a partition. When you are
learning trajectories, each partition will eventually become a separate
trajectory. We run cluster_cells() as before.
cds <- cluster_cells(cds)
p3 <- plot_cells(cds, color_cells_by = "partition")
ggsave("figures/monocle3_trajectory_partitions.pdf", p3, width=12, height=10)
ggsave("figures/monocle3_trajectory_partitions.jpg", p3, width=12, height=10)
Figure 3: UMAP of C. elegans embryonic cells colored by
partition
πΈοΈ Learn the trajectory
graph
Next, we will fit a principal graph within each partition using the
learn_graph() function:
p4 <- plot_cells(cds,
color_cells_by = "cell.type",
label_groups_by_cluster=FALSE,
label_leaves=FALSE,
label_branch_points=FALSE)
ggsave("figures/monocle3_trajectory_graph.pdf", p4, width=12, height=10)
ggsave("figures/monocle3_trajectory_graph.jpg", p4, width=12, height=10)
Figure 4: UMAP of C. elegans embryonic cells with
learned trajectory graph
This graph will be used in many downstream steps, such as branch
analysis and differential expression.
β³ Order the cells in
pseudotime
Once weβve learned a graph, we are ready to order the cells according
to their progress through the developmental program. Monocle measures
this progress in pseudotime.
What is pseudotime?
Pseudotime is a measure of how much progress an individual cell has
made through a process such as cell differentiation.
In many biological processes, cells do not progress in perfect
synchrony. In single-cell expression studies of processes such as cell
differentiation, captured cells might be widely distributed in terms of
progress. That is, in a population of cells captured at exactly the same
time, some cells might be far along, while others might not yet even
have begun the process. This asynchrony creates major problems when you
want to understand the sequence of regulatory changes that occur as
cells transition from one state to the next. Tracking the expression
across cells captured at the same time produces a very compressed sense
of a geneβs kinetics, and the apparent variability of that geneβs
expression will be very high.
By ordering each cell according to its progress along a learned
trajectory, Monocle alleviates the problems that arise due to
asynchrony. Instead of tracking changes in expression as a function of
time, Monocle tracks changes as a function of progress along the
trajectory, which we term βpseudotimeβ. Pseudotime is an abstract unit
of progress: itβs simply the distance between a cell and the start of
the trajectory, measured along the shortest path. The trajectoryβs total
length is defined in terms of the total amount of transcriptional change
that a cell undergoes as it moves from the starting state to the end
state.
In order to place the cells in order, we need to tell Monocle where
the βbeginningβ of the biological process is. We do so by choosing
regions of the graph that we mark as βrootsβ of the trajectory. In time
series experiments, this can usually be accomplished by finding spots in
the UMAP space that are occupied by cells from early time points:
p5 <- plot_cells(cds,
color_cells_by = "embryo.time.bin",
label_cell_groups=FALSE,
label_leaves=TRUE,
label_branch_points=TRUE,
graph_label_size=1.5)
ggsave("figures/monocle3_trajectory_time_bin.pdf", p5, width=12, height=10)
ggsave("figures/monocle3_trajectory_time_bin.jpg", p5, width=12, height=10)
Figure 5: UMAP of C. elegans embryonic cells colored by
embryo time bin
The black lines show the structure of the graph. Note that the graph
is not fully connected: cells in different partitions are in distinct
components of the graph. The circles with numbers in them denote special
points within the graph. Each leaf, denoted by light gray circles,
corresponds to a different outcome (i.e.Β cell fate) of the trajectory.
Black circles indicate branch nodes, in which cells can travel to one of
several outcomes. You can control whether or not these are shown in the
plot with the label_leaves and
label_branch_points arguments to plot_cells.
Please note that numbers within the circles are provided for reference
purposes only.
Now that we have a sense of where the early cells fall, we can call
order_cells(), which will calculate where each cell falls
in pseudotime. In order to do so order_cells() needs you to
specify the root nodes of the trajectory graph. If you donβt provide
them as an argument, it will launch a graphical user interface for
selecting one or more root nodes.
# a helper function to identify the root principal points:
get_earliest_principal_node <- function(cds, time_bin="130-170"){
cell_ids <- which(colData(cds)[, "embryo.time.bin"] == time_bin)
closest_vertex <-
cds@principal_graph_aux[["UMAP"]]$pr_graph_cell_proj_closest_vertex
closest_vertex <- as.matrix(closest_vertex[colnames(cds), ])
root_pr_nodes <-
igraph::V(principal_graph(cds)[["UMAP"]])$name[as.numeric(names
(which.max(table(closest_vertex[cell_ids,]))))]
root_pr_nodes
}
cds <- order_cells(cds, root_pr_nodes=get_earliest_principal_node(cds))
In the above example, we just chose one location, but you could pick
as many as you want. Plotting the cells and coloring them by pseudotime
shows how they were ordered:
p6 <- plot_cells(cds,
color_cells_by = "pseudotime",
label_cell_groups=FALSE,
label_leaves=FALSE,
label_branch_points=FALSE,
graph_label_size=1.5)
ggsave("figures/monocle3_trajectory_pseudotime_interactive.pdf", p6, width=12, height=10)
ggsave("figures/monocle3_trajectory_pseudotime_interactive.jpg", p6, width=12, height=10)
Figure 6: UMAP of C. elegans embryonic cells colored by
pseudotime
Note that some of the cells are gray. This means they have infinite
pseudotime, because they were not reachable from the root nodes that
were picked. In general, any cell on a partition that lacks a root node
will be assigned an infinite pseudotime. In general, you should choose
at least one root per partition.
Itβs often desirable to specify the root of the trajectory
programmatically, rather than manually picking it. The function below
does so by first grouping the cells according to which trajectory graph
node they are nearest to. Then, it calculates what fraction of the cells
at each node come from the earliest time point. Then it picks the node
that is most heavily occupied by early cells and returns that as the
root.
# a helper function to identify the root principal points:
get_earliest_principal_node <- function(cds, time_bin="130-170"){
cell_ids <- which(colData(cds)[, "embryo.time.bin"] == time_bin)
closest_vertex <-
cds@principal_graph_aux[["UMAP"]]$pr_graph_cell_proj_closest_vertex
closest_vertex <- as.matrix(closest_vertex[colnames(cds), ])
root_pr_nodes <-
igraph::V(principal_graph(cds)[["UMAP"]])$name[as.numeric(names
(which.max(table(closest_vertex[cell_ids,]))))]
root_pr_nodes
}
cds <- order_cells(cds, root_pr_nodes=get_earliest_principal_node(cds))
Passing the programatically selected root node to
order_cells() via the root_pr_nodes argument
yields:
p7 <- plot_cells(cds,
color_cells_by = "pseudotime",
label_cell_groups=FALSE,
label_leaves=FALSE,
label_branch_points=FALSE,
graph_label_size=1.5)
ggsave("figures/monocle3_trajectory_pseudotime_programmatic.pdf", p7, width=12, height=10)
ggsave("figures/monocle3_trajectory_pseudotime_programmatic.jpg", p7, width=12, height=10)
Figure 7: UMAP of C. elegans embryonic cells colored by
pseudotime
Note that we could easily do this on a per-partition basis by first
grouping the cells by partition using the partitions()
function. This would result in all cells being assigned a finite
pseudotime.
πΏ Subset cells by
branch
It is often useful to subset cells based on their branch in the
trajectory. The function choose_graph_segments allows you
to do so interactively.
cds_sub <- choose_graph_segments(cds)
π§ Working with 3D
trajectories
You can also perform trajectory analysis in 3D.
cds_3d <- reduce_dimension(cds, max_components = 3)
cds_3d <- cluster_cells(cds_3d)
cds_3d <- learn_graph(cds_3d)
cds_3d <- order_cells(cds_3d, root_pr_nodes=get_earliest_principal_node(cds))
# Get the UMAP coordinates and partition information
umap_coords <- reducedDims(cds_3d)$UMAP
cell_partitions <- partitions(cds_3d)
# Create a data frame for plotting
plot_df <- data.frame(
UMAP_1 = umap_coords[,1],
UMAP_2 = umap_coords[,2],
UMAP_3 = umap_coords[,3],
Partition = factor(cell_partitions)
)
# Create and display interactive 3D plot with plotly
# Create the 3D plot
fig <- plot_ly(plot_df,
x = ~UMAP_1,
y = ~UMAP_2,
z = ~UMAP_3,
color = ~Partition,
type = "scatter3d",
mode = "markers",
marker = list(size = 3)) %>%
layout(scene = list(
xaxis = list(title = "UMAP_1"),
yaxis = list(title = "UMAP_2"),
zaxis = list(title = "UMAP_3")
))
# Display the plot directly
fig
References
Cao, J., M. Spielmann, X. Qiu, X. Huang, D. M. Ibrahim, A. J. Hill, F.
Zhang, et al. 2019.
βThe Single-Cell Transcriptional Landscape of
Mammalian Organogenesis.β Journal Article.
Nature 566
(7745): 496β502.
https://doi.org/10.1038/s41586-019-0969-x.
Packer, J. S., Q. Zhu, C. Huynh, P. Sivaramakrishnan, E. Preston, H.
Dueck, D. Stefanik, et al. 2019.
βA Lineage-Resolved Molecular
Atlas of c. Elegans Embryogenesis at Single-Cell Resolution.β
Journal Article.
Science 365 (6459).
https://doi.org/10.1126/science.aax1971.
Saelens, W., R. Cannoodt, H. Todorov, and Y. Saeys. 2019.
βA
Comparison of Single-Cell Trajectory Inference Methods.β Journal
Article.
Nat Biotechnol 37 (5): 547β54.
https://doi.org/10.1038/s41587-019-0071-9.
Trapnell, C., D. Cacchiarelli, J. Grimsby, P. Pokharel, S. Li, M. Morse,
N. J. Lennon, K. J. Livak, T. S. Mikkelsen, and J. L. Rinn. 2014.
βThe Dynamics and Regulators of Cell Fate Decisions Are Revealed
by Pseudotemporal Ordering of Single Cells.β Journal Article.
Nat Biotechnol 32 (4): 381β86.
https://doi.org/10.1038/nbt.2859.
LS0tDQp0aXRsZTogIvCfj5fvuI8gQ29uc3RydWN0aW5nIHNpbmdsZS1jZWxsIHRyYWplY3RvcmllcyB3aXRoIE1vbm9jbGUz8J+nnCINCmF1dGhvcjogIkhvbmdjaGVuIFhpYW8iDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpiaWJsaW9ncmFwaHk6IFsiQmlvaW5mb3JtYXRpY3MuYmliIl0NCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiANCiAgICAgIGNvbGxhcHNlZDogVFJVRQ0KICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIHRoZW1lOiBjb3Ntbw0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgICBkZl9wcmludDogcGFnZWQNCiAgICBmaWdfY2FwdGlvbjogdHJ1ZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCi0tLQ0KDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICBlY2hvID0gVFJVRSwNCiAgd2FybmluZyA9IEZBTFNFLA0KICBtZXNzYWdlID0gRkFMU0UsDQogIGVycm9yID0gRkFMU0UsDQogIGNvbW1lbnQgPSAiIz4iLA0KICBmaWcuYWxpZ24gPSAiY2VudGVyIiwNCiAgb3V0LndpZHRoID0gIjgwJSIsDQogIGRwaSA9IDMwMCwNCiAgY2FjaGUgPSBUUlVFDQopDQoNCmBgYA0KDQoNCmBgYHtyIGtsaXBweSwgZWNobz1GQUxTRSwgaW5jbHVkZT1UUlVFfQ0KbGlicmFyeShrbGlwcHkpDQogICAga2xpcHB5OjprbGlwcHkocG9zaXRpb24gPSBjKCdib3R0b20nLCAncmlnaHQnKSwNCiAgICAgICAgICAgICAgICAgICBsYW5nID0gYygncicsICdweXRob24nLCAnYmFzaCcpLA0KICAgICAgICAgICAgICAgICAgIHRvb2x0aXBfbWVzc2FnZSA9ICdDbGljayB0byBjb3B5JywgDQogICAgICAgICAgICAgICAgICAgdG9vbHRpcF9zdWNjZXNzID0gJ0NvcGllZCEnLA0KICAgICAgICAgICAgICAgICAgIGNvbG9yID0gJ2RhcmtSZWQnKQ0KYGBgDQoNCg0KYGBge2NzcywgZWNobz1GQUxTRX0NCi8qIEN1c3RvbSBDU1Mgc3R5bGluZyAqLw0KYm9keSB7DQogIGZvbnQtZmFtaWx5OiAnSGVsdmV0aWNhIE5ldWUnLCBBcmlhbCwgc2Fucy1zZXJpZjsNCiAgbGluZS1oZWlnaHQ6IDEuODsNCiAgbWF4LXdpZHRoOiAxMjAwcHg7DQogIG1hcmdpbjogYXV0bzsNCiAgcGFkZGluZzogMmVtOw0KICBiYWNrZ3JvdW5kLWNvbG9yOiAjZmZmZmZmOw0KICBjb2xvcjogIzJjM2U1MDsNCn0NCg0KaDEsIGgyLCBoMywgaDQgew0KICBjb2xvcjogIzJjM2U1MDsNCiAgZm9udC13ZWlnaHQ6IDYwMDsNCiAgbWFyZ2luLXRvcDogMmVtOw0KICBib3JkZXItYm90dG9tOiAycHggc29saWQgI2VhZWNlZjsNCiAgcGFkZGluZy1ib3R0b206IDAuM2VtOw0KfQ0KDQoudGl0bGUgew0KICBjb2xvcjogIzJjM2U1MDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBmb250LXNpemU6IDIuOGVtOw0KICBtYXJnaW4tYm90dG9tOiAxZW07DQogIGJvcmRlci1ib3R0b206IG5vbmU7DQogIGZvbnQtd2VpZ2h0OiA3MDA7DQp9DQoNCi5hdXRob3IsIC5kYXRlIHsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBjb2xvcjogIzdmOGM4ZDsNCiAgbWFyZ2luLWJvdHRvbTogMmVtOw0KfQ0KDQovKiBDb2RlIGJsb2NrcyAqLw0KcHJlIHsNCiAgYmFja2dyb3VuZC1jb2xvcjogI2Y4ZjlmYSAhaW1wb3J0YW50Ow0KICBib3JkZXI6IDFweCBzb2xpZCAjZTllY2VmOw0KICBib3JkZXItcmFkaXVzOiA0cHg7DQogIHBhZGRpbmc6IDFlbTsNCiAgbWFyZ2luOiAxZW0gMDsNCiAgb3ZlcmZsb3cteDogYXV0bzsNCn0NCg0KY29kZSB7DQogIGNvbG9yOiAjZTgzZThjOw0KICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjhmOWZhOw0KICBwYWRkaW5nOiAycHggNHB4Ow0KICBib3JkZXItcmFkaXVzOiAzcHg7DQp9DQoNCnByZS5yOmJlZm9yZSB7DQogIGNvbnRlbnQ6ICJSIFNjcmlwdCI7DQogIGRpc3BsYXk6IGJsb2NrOyAvKiBNYWtlIHRoZSBsYWJlbCBhcHBlYXIgb24gaXRzIG93biBsaW5lICovDQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBjb2xvcjogIzU1NTsNCiAgYmFja2dyb3VuZC1jb2xvcjogI2VlZTsNCiAgcGFkZGluZzogNXB4DQp9DQoNCnByZS5iYXNoOmJlZm9yZSB7DQogIGNvbnRlbnQ6ICJCYXNoIFNjcmlwdCI7DQogIGRpc3BsYXk6IGJsb2NrOyAvKiBNYWtlIHRoZSBsYWJlbCBhcHBlYXIgb24gaXRzIG93biBsaW5lICovDQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBjb2xvcjogIzU1NTsNCiAgYmFja2dyb3VuZC1jb2xvcjogI2VlZTsNCiAgcGFkZGluZzogNXB4Ow0KfQ0KDQovKiBUYWJsZSBvZiBDb250ZW50cyBzdHlsaW5nICovDQojVE9DIHsNCiAgcGFkZGluZzogMjBweDsNCiAgYmFja2dyb3VuZDogI2Y4ZjlmYTsNCiAgYm9yZGVyLXJhZGl1czogNXB4Ow0KICBib3JkZXI6IDFweCBzb2xpZCAjZTllY2VmOw0KfQ0KDQoudG9jaWZ5IHsNCiAgYm9yZGVyOiBub25lOw0KfQ0KDQoudG9jaWZ5IC50b2NpZnktaXRlbSBhIHsNCiAgY29sb3I6ICMyYzNlNTA7DQp9DQoNCi50b2NpZnkgLnRvY2lmeS1pdGVtLmFjdGl2ZSBhIHsNCiAgY29sb3I6ICMwMDdiZmY7DQogIGJvcmRlci1sZWZ0OiAzcHggc29saWQgIzAwN2JmZjsNCn0NCg0KLyogSW1hZ2VzICovDQppbWcgew0KICBtYXgtd2lkdGg6IDEwMCU7DQogIGhlaWdodDogYXV0bzsNCiAgbWFyZ2luOiAyZW0gYXV0bzsNCiAgZGlzcGxheTogYmxvY2s7DQogIGJveC1zaGFkb3c6IDAgNHB4IDZweCByZ2JhKDAsIDAsIDAsIDAuMSk7DQogIGJvcmRlci1yYWRpdXM6IDRweDsNCn0NCg0KLyogTGlzdHMgKi8NCnVsLCBvbCB7DQogIHBhZGRpbmctbGVmdDogMS41ZW07DQogIG1hcmdpbi1ib3R0b206IDEuNWVtOw0KfQ0KDQpsaSB7DQogIG1hcmdpbi1ib3R0b206IDAuNWVtOw0KfQ0KDQovKiBCbG9ja3F1b3RlcyAqLw0KYmxvY2txdW90ZSB7DQogIGJvcmRlci1sZWZ0OiA0cHggc29saWQgIzAwN2JmZjsNCiAgcGFkZGluZy1sZWZ0OiAxZW07DQogIG1hcmdpbjogMWVtIDA7DQogIGNvbG9yOiAjNmM3NTdkOw0KfQ0KDQovKiBUYWJsZXMgKi8NCnRhYmxlIHsNCiAgd2lkdGg6IDEwMCU7DQogIG1hcmdpbjogMmVtIDA7DQogIGJvcmRlci1jb2xsYXBzZTogY29sbGFwc2U7DQp9DQoNCnRoLCB0ZCB7DQogIHBhZGRpbmc6IDEycHg7DQogIGJvcmRlcjogMXB4IHNvbGlkICNlOWVjZWY7DQp9DQoNCnRoIHsNCiAgYmFja2dyb3VuZC1jb2xvcjogI2Y4ZjlmYTsNCiAgZm9udC13ZWlnaHQ6IDYwMDsNCn0NCg0KLyogTGlua3MgKi8NCmEgew0KICBjb2xvcjogIzAwN2JmZjsNCiAgdGV4dC1kZWNvcmF0aW9uOiBub25lOw0KfQ0KDQphOmhvdmVyIHsNCiAgY29sb3I6ICMwMDU2YjM7DQogIHRleHQtZGVjb3JhdGlvbjogdW5kZXJsaW5lOw0KfQ0KDQovKiBTZWN0aW9uIHNwYWNpbmcgKi8NCnNlY3Rpb24gew0KICBtYXJnaW4tYm90dG9tOiAzZW07DQp9DQoNCi8qIENvZGUgb3V0cHV0ICovDQoub3V0cHV0IHsNCiAgYmFja2dyb3VuZC1jb2xvcjogI2Y4ZjlmYTsNCiAgYm9yZGVyOiAxcHggc29saWQgI2U5ZWNlZjsNCiAgYm9yZGVyLXJhZGl1czogNHB4Ow0KICBwYWRkaW5nOiAxZW07DQogIG1hcmdpbi10b3A6IDFlbTsNCn0NCg0KLyogV2FybmluZyBhbmQgbm90ZSBib3hlcyAqLw0KLm5vdGUsIC53YXJuaW5nIHsNCiAgcGFkZGluZzogMWVtOw0KICBtYXJnaW46IDFlbSAwOw0KICBib3JkZXItcmFkaXVzOiA0cHg7DQp9DQoNCi5ub3RlIHsNCiAgYmFja2dyb3VuZC1jb2xvcjogI2UzZjJmZDsNCiAgYm9yZGVyOiAxcHggc29saWQgIzkwY2FmOTsNCn0NCg0KLndhcm5pbmcgew0KICBiYWNrZ3JvdW5kLWNvbG9yOiAjZmZmM2UwOw0KICBib3JkZXI6IDFweCBzb2xpZCAjZmZiNzRkOw0KfQ0KDQpgYGANCg0KIyMg8J+UgyBMb2FkIHJlcXVpcmVkIGxpYnJhcmllcw0KDQpgYGB7ciBsb2FkLWxpYnJhcmllc30NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoaHRtbHdpZGdldHMpDQpsaWJyYXJ5KGlncmFwaCkNCmxpYnJhcnkobW9ub2NsZTMpDQpsaWJyYXJ5KHBsb3RseSkNCg0Kc2V0LnNlZWQoMTIzNDUpDQpgYGANCg0KIyMg8J+nrCBDb25zdHJ1Y3Rpbmcgc2luZ2xlLWNlbGwgdHJhamVjdG9yaWVzDQoNCkR1cmluZyBkZXZlbG9wbWVudCwgaW4gcmVzcG9uc2UgdG8gc3RpbXVsaSwgYW5kIHRocm91Z2hvdXQgbGlmZSwgY2VsbHMgdHJhbnNpdGlvbiBmcm9tIG9uZSBmdW5jdGlvbmFsICJzdGF0ZSIgdG8gYW5vdGhlci4gQ2VsbHMgaW4gZGlmZmVyZW50IHN0YXRlcyBleHByZXNzIGRpZmZlcmVudCBzZXRzIG9mIGdlbmVzLCBwcm9kdWNpbmcgYSBkeW5hbWljIHJlcGV0b2lyZSBvZiBwcm90ZWlucyBhbmQgbWV0YWJvbGl0ZXMgdGhhdCBjYXJyeSBvdXQgdGhlaXIgd29yay4gQXMgY2VsbHMgbW92ZSBiZXR3ZWVuIHN0YXRlcywgdGhleSB1bmRlcmdvIGEgcHJvY2VzcyBvZiB0cmFuc2NyaXB0aW9uYWwgcmUtY29uZmlndXJhdGlvbiwgd2l0aCBzb21lIGdlbmVzIGJlaW5nIHNpbGVuY2VkIGFuZCBvdGhlcnMgbmV3bHkgYWN0aXZhdGVkLiBUaGVzZSB0cmFuc2llbnQgc3RhdGVzIGFyZSBvZnRlbiBoYXJkIHRvIGNoYXJhY3Rlcml6ZSBiZWNhdXNlIHB1cmlmeWluZyBjZWxscyBpbiBiZXR3ZWVuIG1vcmUgc3RhYmxlIGVuZHBvaW50IHN0YXRlcyBjYW4gYmUgZGlmZmljdWx0IG9yIGltcG9zc2libGUuIFNpbmdsZS1jZWxsIFJOQS1TZXEgY2FuIGVuYWJsZSB5b3UgdG8gc2VlIHRoZXNlIHN0YXRlcyB3aXRob3V0IHRoZSBuZWVkIGZvciBwdXJpZmljYXRpb24uIEhvd2V2ZXIsIHRvIGRvIHNvLCB3ZSBtdXN0IGRldGVybWluZSB3aGVyZSBlYWNoIGNlbGwgaXMgaW4gdGhlIHJhbmdlIG9mIHBvc3NpYmxlIHN0YXRlcy4gRm9yIGEgY29tcGFyaXNvbiBvZiBkaWZmZXJlbnQgdHJhamVjdG9yeSBpbmZlcmVuY2UgbWV0aG9kcywgc2VlIFNhZWxlbnMgZXQgYWwuICgyMDE5KSBbQFJOMzBdLg0KDQpNb25vY2xlIDMgW0BSTjc1XSBpcyB0aGUgbGF0ZXN0IHZlcnNpb24gb2YgdGhlIE1vbm9jbGUgdG9vbGtpdC4gTW9ub2NsZSBpbnRyb2R1Y2VkIHRoZSBzdHJhdGVneSBvZiB1c2luZyBSTkEtU2VxIGZvciBzaW5nbGUtY2VsbCB0cmFqZWN0b3J5IGFuYWx5c2lzIFtAUk43Nl0uIFJhdGhlciB0aGFuIHB1cmlmeWluZyBjZWxscyBpbnRvIGRpc2NyZXRlIHN0YXRlcyBleHBlcmltZW50YWxseSwgTW9ub2NsZSB1c2VzIGFuIGFsZ29yaXRobSB0byBsZWFybiB0aGUgc2VxdWVuY2Ugb2YgZ2VuZSBleHByZXNzaW9uIGNoYW5nZXMgZWFjaCBjZWxsIG11c3QgZ28gdGhyb3VnaCBhcyBwYXJ0IG9mIGEgZHluYW1pYyBiaW9sb2dpY2FsIHByb2Nlc3MuIE9uY2UgaXQgaGFzIGxlYXJuZWQgdGhlIG92ZXJhbGwgInRyYWplY3RvcnkiIG9mIGdlbmUgZXhwcmVzc2lvbiBjaGFuZ2VzLCBNb25vY2xlIGNhbiBwbGFjZSBlYWNoIGNlbGwgYXQgaXRzIHByb3BlciBwb3NpdGlvbiBpbiB0aGUgdHJhamVjdG9yeS4gWW91IGNhbiB0aGVuIHVzZSBNb25vY2xlJ3MgZGlmZmVyZW50aWFsIGFuYWx5c2lzIHRvb2xraXQgdG8gZmluZCBnZW5lcyByZWd1bGF0ZWQgb3ZlciB0aGUgY291cnNlIG9mIHRoZSB0cmFqZWN0b3J5LCBhcyBkZXNjcmliZWQgaW4gdGhlIHNlY3Rpb24gKkZpbmRpbmcgZ2VuZXMgdGhhdCBjaGFuZ2UgYXMgYSBmdW5jdGlvbiBvZiBwc2V1ZG90aW1lKi4gSWYgdGhlcmUgYXJlIG11bHRpcGxlIG91dGNvbWVzIGZvciB0aGUgcHJvY2VzcywgTW9ub2NsZSB3aWxsIHJlY29uc3RydWN0IGEgImJyYW5jaGVkIiB0cmFqZWN0b3J5LiBUaGVzZSBicmFuY2hlcyBjb3JyZXNwb25kIHRvIGNlbGx1bGFyICJkZWNpc2lvbnMiLCBhbmQgTW9ub2NsZSBwcm92aWRlcyBwb3dlcmZ1bCB0b29scyBmb3IgaWRlbnRpZnlpbmcgdGhlIGdlbmVzIGFmZmVjdGVkIGJ5IHRoZW0gYW5kIGludm9sdmVkIGluIG1ha2luZyB0aGVtLiBZb3UgY2FuIHNlZSBob3cgdG8gYW5hbHl6ZSBicmFuY2hlcyBpbiB0aGUgc2VjdGlvbiAqQW5hbHl6aW5nIGJyYW5jaGVzIGluIHNpbmdsZS1jZWxsIHRyYWplY3RvcmllcyouDQoNClRoZSB3b3JrZmxvdyBmb3IgcmVjb25zdHJ1Y3RpbmcgdHJhamVjdG9yaWVzIGlzIHZlcnkgc2ltaWxhciB0byB0aGUgd29ya2Zsb3cgZm9yIGNsdXN0ZXJpbmcsIGJ1dCBpdCBoYXMgYSBmZXcgYWRkaXRpb25hbCBzdGVwcy4gVG8gaWxsdXN0cmF0ZSB0aGUgd29ya2Zsb3csIHdlIHdpbGwgdXNlIGFub3RoZXIgKkMuIGVsZWdhbnMqIGRhdGEgc2V0LCB0aGlzIG9uZSBmcm9tIFBhY2tlciAmIFpodSBldCBhbC4gW0BSTjc3XS4gVGhlaXIgc3R1ZHkgaW5jbHVkZXMgYSB0aW1lIHNlcmllcyBhbmFseXNpcyBvZiB3aG9sZSBkZXZlbG9waW5nIGVtYnlyb3MuIFdlIHdpbGwgZXhhbWluZSBhIHNtYWxsIHN1YnNldCBvZiB0aGUgZGF0YSB3aGljaCBpbmNsdWRlcyBtb3N0IG9mIHRoZSBuZXVyb25zLg0KDQojIyDwn5OlIExvYWRpbmcgdGhlIGRhdGENCg0KV2Ugd2lsbCBsb2FkIHRoZSBkYXRhIGFzIHdlIGRpZCB3aXRoIHRoZSBMMiBkYXRhLiBUaGlzIGRhdGFzZXQgY29udGFpbnMgYW4gZXhwcmVzc2lvbiBtYXRyaXgsIGNlbGwgbWV0YWRhdGEsIGFuZCBnZW5lIGFubm90YXRpb25zLg0KDQpgYGB7ciBsb2FkLWRhdGF9DQpleHByZXNzaW9uX21hdHJpeCA8LSByZWFkUkRTKHVybCgiaHR0cHM6Ly9kZXB0cy53YXNoaW5ndG9uLmVkdTovdHJhcG5lbGwtbGFiL3NvZnR3YXJlL21vbm9jbGUzL2NlbGVnYW5zL2RhdGEvcGFja2VyX2VtYnJ5b19leHByZXNzaW9uLnJkcyIpKQ0KY2VsbF9tZXRhZGF0YSA8LSByZWFkUkRTKHVybCgiaHR0cHM6Ly9kZXB0cy53YXNoaW5ndG9uLmVkdTovdHJhcG5lbGwtbGFiL3NvZnR3YXJlL21vbm9jbGUzL2NlbGVnYW5zL2RhdGEvcGFja2VyX2VtYnJ5b19jb2xEYXRhLnJkcyIpKQ0KZ2VuZV9hbm5vdGF0aW9uIDwtIHJlYWRSRFModXJsKCJodHRwczovL2RlcHRzLndhc2hpbmd0b24uZWR1Oi90cmFwbmVsbC1sYWIvc29mdHdhcmUvbW9ub2NsZTMvY2VsZWdhbnMvZGF0YS9wYWNrZXJfZW1icnlvX3Jvd0RhdGEucmRzIikpDQoNCmNkcyA8LSBuZXdfY2VsbF9kYXRhX3NldChleHByZXNzaW9uX21hdHJpeCwNCiAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsX21ldGFkYXRhID0gY2VsbF9tZXRhZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX21ldGFkYXRhID0gZ2VuZV9hbm5vdGF0aW9uKQ0KYGBgDQoNCiMjIOKame+4jyBQcmUtcHJvY2VzcyB0aGUgZGF0YQ0KDQpQcmUtcHJvY2Vzc2luZyB3b3JrcyBleGFjdGx5IGFzIGluIGNsdXN0ZXJpbmcgYW5hbHlzaXMuIFRoaXMgdGltZSwgd2Ugd2lsbCB1c2UgYSBkaWZmZXJlbnQgc3RyYXRlZ3kgZm9yIGJhdGNoIGNvcnJlY3Rpb24sIHdoaWNoIGluY2x1ZGVzIHdoYXQgUGFja2VyICYgWmh1IGV0IGFsIGRpZCBpbiB0aGVpciBvcmlnaW5hbCBhbmFseXNpcy4NCg0KKipOb3RlOioqIFlvdXIgZGF0YSB3aWxsIG5vdCBoYXZlIHRoZSBsb2FkaW5nIGJhdGNoIGluZm9ybWF0aW9uIGRlbW9uc3RyYXRlZCBoZXJlLCB5b3Ugd2lsbCBjb3JyZWN0IGJhdGNoIHVzaW5nIHlvdXIgb3duIGJhdGNoIGluZm9ybWF0aW9uLg0KDQpgYGB7ciBwcmVwcm9jZXNzLWRhdGF9DQpjZHMgPC0gcHJlcHJvY2Vzc19jZHMoY2RzLCBudW1fZGltID0gNTApDQpjZHMgPC0gYWxpZ25fY2RzKGNkcywgYWxpZ25tZW50X2dyb3VwID0gImJhdGNoIiwgcmVzaWR1YWxfbW9kZWxfZm9ybXVsYV9zdHIgPSAifiBiZy4zMDAubG9hZGluZyArIGJnLjQwMC5sb2FkaW5nICsgYmcuNTAwLjEubG9hZGluZyArIGJnLjUwMC4yLmxvYWRpbmcgKyBiZy5yMTcubG9hZGluZyArIGJnLmIwMS5sb2FkaW5nICsgYmcuYjAyLmxvYWRpbmciKQ0KYGBgDQoNCk5vdGUgdGhhdCBpbiBhZGRpdGlvbiB0byB1c2luZyB0aGUgYGFsaWdubWVudF9ncm91cGAgYXJndW1lbnQgdG8gYGFsaWduX2NkcygpYCwgd2hpY2ggYWxpZ25zIGdyb3VwcyBvZiBjZWxscyAoaS5lLiBiYXRjaGVzKSwgd2UgYXJlIGFsc28gdXNpbmcgYHJlc2lkdWFsX21vZGVsX2Zvcm11bGFfc3RyYC4gVGhpcyBhcmd1bWVudCBpcyBmb3Igc3VidHJhY3RpbmcgY29udGludW91cyBlZmZlY3RzLiBZb3UgY2FuIHVzZSB0aGlzIHRvIGNvbnRyb2wgZm9yIHRoaW5ncyBsaWtlIHRoZSBmcmFjdGlvbiBvZiBtaXRvY2hvbmRyaWFsIHJlYWRzIGluIGVhY2ggY2VsbCwgd2hpY2ggaXMgc29tZXRpbWVzIHVzZWQgYXMgYSBRQyBtZXRyaWMgZm9yIGVhY2ggY2VsbC4gSW4gdGhpcyBleHBlcmltZW50IChhcyBpbiBtYW55IHNjUk5BLXNlcSBleHBlcmltZW50cyksIHNvbWUgY2VsbHMgc3BvbnRhbm91c2x5IGx5c2UsIHJlbGVhc2luZyB0aGVpciBtUk5BcyBpbnRvIHRoZSBjZWxsIHN1c3BlbnNpb24gaW1tZWRpYXRlbHkgcHJpb3IgdG8gbG9hZGluZyBpbnRvIHRoZSBzaW5nbGUtY2VsbCBsaWJyYXJ5IHByZXAuIFRoaXMgInN1cGVybmF0YW50IFJOQSIgY29udGFtaW5hdGVzIGVhY2ggY2VsbHMnIHRyYW5zY3JpcHRvbWUgcHJvZmlsZSB0byBhIGNlcnRhaW4gZXh0ZW50LiBGb3J0dW5hdGVseSwgaXQgaXMgZmFpcmx5IHN0cmFpZ2h0Zm9yd2FyZCB0byBlc3RpbWF0ZSB0aGUgbGV2ZWwgb2YgYmFja2dyb3VuZCBjb250YW1pbmF0aW9uIGluIGVhY2ggYmF0Y2ggb2YgY2VsbHMgYW5kIHN1YnRyYWN0IGl0LCB3aGljaCBpcyB3aGF0IFBhY2tlciBldCBhbCBkaWQgaW4gdGhlIG9yaWdpbmFsIHN0dWR5LiBFYWNoIG9mIHRoZSBjb2x1bW5zIGBiZy4zMDAubG9hZGluZ2AsIGBiZy40MDAubG9hZGluZ2AsIGNvcnJlc3BvbmRzIHRvIGEgYmFja2dyb3VuZCBzaWduYWwgdGhhdCBhIGNlbGwgbWlnaHQgYmUgY29udGFtaW5hdGVkIHdpdGguIFBhc3NpbmcgdGhlc2UgY29sdW1zIGFzIHRlcm1zIGluIHRoZSBgcmVzaWR1YWxfbW9kZWxfZm9ybXVsYV9zdHJgIHRlbGxzIGBhbGlnbl9jZHMoKWAgdG8gc3VidHJhY3QgdGhlc2Ugc2lnbmFscyBwcmlvciB0byBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24sIGNsdXN0ZXJpbmcsIGFuZCB0cmFqZWN0b3J5IGluZmVyZW5jZS4gTm90ZSB0aGF0IHlvdSBjYW4gY2FsbCBgYWxpZ25fY2RzKClgIHdpdGggYGFsaWdubWVudF9ncm91cGAsIGByZXNpZHVhbF9tb2RlbF9mb3JtdWxhYCwgb3IgYm90aC4NCg0KIyMg8J+OqCBSZWR1Y2UgZGltZW5zaW9uYWxpdHkgYW5kIHZpc3VhbGl6ZSB0aGUgcmVzdWx0cw0KDQpOZXh0LCB3ZSByZWR1Y2UgdGhlIGRpbWVuc2lvbmFsaXR5IG9mIHRoZSBkYXRhLiBIb3dldmVyLCB1bmxpa2UgY2x1c3RlcmluZywgd2hpY2ggd29ya3Mgd2VsbCB3aXRoIGJvdGggVU1BUCBhbmQgdC1TTkUsIGhlcmUgd2Ugc3Ryb25nbHkgdXJnZSB5b3UgdG8gdXNlIFVNQVAsIHRoZSBkZWZhdWx0IG1ldGhvZDoNCg0KYGBge3IgcmVkdWNlLWRpbWVuc2lvbmFsaXR5fQ0KY2RzIDwtIHJlZHVjZV9kaW1lbnNpb24oY2RzKQ0KcDEgPC0gcGxvdF9jZWxscyhjZHMsIGxhYmVsX2dyb3Vwc19ieV9jbHVzdGVyPUZBTFNFLCAgY29sb3JfY2VsbHNfYnkgPSAiY2VsbC50eXBlIikNCmdnc2F2ZSgiZmlndXJlcy9tb25vY2xlM190cmFqZWN0b3J5X3VtYXAucGRmIiwgcDEsIHdpZHRoPTEyLCBoZWlnaHQ9MTApDQpnZ3NhdmUoImZpZ3VyZXMvbW9ub2NsZTNfdHJhamVjdG9yeV91bWFwLmpwZyIsIHAxLCB3aWR0aD0xMiwgaGVpZ2h0PTEwKQ0KYGBgDQoNCjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsiPg0KPGltZyBzcmM9ImZpZ3VyZXMvbW9ub2NsZTNfdHJhamVjdG9yeV91bWFwLmpwZyIgYWx0PSJVTUFQIG9mIEMuIGVsZWdhbnMgZW1icnlvbmljIGNlbGxzIGNvbG9yZWQgYnkgY2VsbCB0eXBlIiBzdHlsZT0id2lkdGg6IDgwJTsiLz4NCjxwIGNsYXNzPSJjYXB0aW9uIj4qKkZpZ3VyZSAxOioqIFVNQVAgb2YgQy4gZWxlZ2FucyBlbWJyeW9uaWMgY2VsbHMgY29sb3JlZCBieSBjZWxsIHR5cGU8L3A+ICANCjwvZGl2Pg0KDQpBcyB5b3UgY2FuIHNlZSwgZGVzcGl0ZSB0aGUgZmFjdCB0aGF0IHdlIGFyZSBvbmx5IGxvb2tpbmcgYXQgYSBzbWFsbCBzbGljZSBvZiB0aGlzIGRhdGFzZXQsIE1vbm9jbGUgcmVjb25zdHJ1Y3RzIGEgdHJhamVjdG9yeSB3aXRoIG51bWVyb3VzIGJyYW5jaGVzLiBPdmVybGF5aW5nIHRoZSBtYW51YWwgYW5ub3RhdGlvbnMgb24gdGhlIFVNQVAgcmV2ZWFscyB0aGF0IHRoZXNlIGJyYW5jaGVzIGFyZSBwcmluY2lwYWxseSBvY2N1cGllZCBieSBvbmUgY2VsbCB0eXBlLg0KDQpBcyB3aXRoIGNsdXN0ZXJpbmcgYW5hbHlzaXMsIHlvdSBjYW4gdXNlIGBwbG90X2NlbGxzKClgIHRvIHZpc3VhbGl6ZSBob3cgaW5kaXZpZHVhbCBnZW5lcyB2YXJ5IGFsb25nIHRoZSB0cmFqZWN0b3J5LiBMZXQncyBsb29rIGF0IHNvbWUgZ2VuZXMgd2l0aCBpbnRlcmVzdGluZyBwYXR0ZXJucyBvZiBleHByZXNzaW9uIGluIGNpbGlhdGVkIG5ldXJvbnM6DQoNCmBgYHtyIHBsb3QtY2lsaWF0ZWQtZ2VuZXN9DQpjaWxpYXRlZF9nZW5lcyA8LSBjKCJjaGUtMSIsDQogICAgICAgICAgICAgICAgICAgICJobGgtMTciLA0KICAgICAgICAgICAgICAgICAgICAibmhyLTYiLA0KICAgICAgICAgICAgICAgICAgICAiZG1kLTYiLA0KICAgICAgICAgICAgICAgICAgICAiY2VoLTM2IiwNCiAgICAgICAgICAgICAgICAgICAgImhhbS0xIikNCg0KcDIgPC0gcGxvdF9jZWxscyhjZHMsDQogICAgICAgICAgIGdlbmVzPWNpbGlhdGVkX2dlbmVzLA0KICAgICAgICAgICBsYWJlbF9jZWxsX2dyb3Vwcz1GQUxTRSwNCiAgICAgICAgICAgc2hvd190cmFqZWN0b3J5X2dyYXBoPUZBTFNFKQ0KZ2dzYXZlKCJmaWd1cmVzL21vbm9jbGUzX3RyYWplY3RvcnlfY2lsaWF0ZWRfZ2VuZXMucGRmIiwgcDIsIHdpZHRoPTEyLCBoZWlnaHQ9MTApDQpnZ3NhdmUoImZpZ3VyZXMvbW9ub2NsZTNfdHJhamVjdG9yeV9jaWxpYXRlZF9nZW5lcy5qcGciLCBwMiwgd2lkdGg9MTIsIGhlaWdodD0xMCkNCmBgYA0KDQo8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBjZW50ZXI7Ij4NCjxpbWcgc3JjPSJmaWd1cmVzL21vbm9jbGUzX3RyYWplY3RvcnlfY2lsaWF0ZWRfZ2VuZXMuanBnIiBhbHQ9IkV4cHJlc3Npb24gb2YgY2lsaWF0ZWQgbmV1cm9uIGdlbmVzIGFsb25nIHRoZSB0cmFqZWN0b3J5IiBzdHlsZT0id2lkdGg6IDgwJTsiLz4NCjxwIGNsYXNzPSJjYXB0aW9uIj4qKkZpZ3VyZSAyOioqIEV4cHJlc3Npb24gb2YgY2lsaWF0ZWQgbmV1cm9uIGdlbmVzIGFsb25nIHRoZSB0cmFqZWN0b3J5PC9wPg0KPC9kaXY+DQoNCg0KDQpXZSB3aWxsIGxlYXJuIGhvdyB0byBpZGVudGlmeSB0aGUgZ2VuZXMgdGhhdCBhcmUgcmVzdHJpY3RlZCB0byBlYWNoIG91dGNvbWUgb2YgdGhlIHRyYWplY3RvcnkgbGF0ZXIgb24gaW4gdGhlIHNlY3Rpb24gKkZpbmRpbmcgZ2VuZXMgdGhhdCBjaGFuZ2UgYXMgYSBmdW5jdGlvbiBvZiBwc2V1ZG90aW1lKi4NCg0KIyMg8J+UrCBDbHVzdGVyIHlvdXIgY2VsbHMNCg0KQWx0aG91Z2ggY2VsbHMgbWF5IGNvbnRpbnVvdXNseSB0cmFuc2l0aW9uIGZyb20gb25lIHN0YXRlIHRvIHRoZSBuZXh0IHdpdGggbm8gZGlzY3JldGUgYm91bmRhcnkgYmV0d2VlbiB0aGVtLCBNb25vY2xlIGRvZXMgbm90IGFzc3VtZSB0aGF0IGFsbCBjZWxscyBpbiB0aGUgZGF0YXNldCBkZXNjZW5kIGZyb20gYSBjb21tb24gdHJhbnNjcmlwdGlvbmFsICJhbmNlc3RvciIuIEluIG1hbnkgZXhwZXJpbWVudHMsIHRoZXJlIG1pZ2h0IGluIGZhY3QgYmUgbXVsdGlwbGUgZGlzdGluY3QgdHJhamVjdG9yaWVzLiBGb3IgZXhhbXBsZSwgaW4gYSB0aXNzdWUgcmVzcG9uZGluZyB0byBhbiBpbmZlY3Rpb24sIHRpc3N1ZSByZXNpZGVudCBpbW11bmUgY2VsbHMgYW5kIHN0cm9tYWwgY2VsbHMgd2lsbCBoYXZlIHZlcnkgZGlmZmVyZW50IGluaXRpYWwgdHJhbnNjcmlwdG9tZXMsIGFuZCB3aWxsIHJlc3BvbmQgdG8gaW5mZWN0aW9uIHF1aXRlIGRpZmZlcmVudGx5LCBzbyB0aGV5IHNob3VsZCBiZSBhIHBhcnQgb2YgdGhlIHNhbWUgdHJhamVjdG9yeS4NCg0KTW9ub2NsZSBpcyBhYmxlIHRvIGxlYXJuIHdoZW4gY2VsbHMgc2hvdWxkIGJlIHBsYWNlZCBpbiB0aGUgc2FtZSB0cmFqZWN0b3J5IGFzIG9wcG9zZWQgdG8gc2VwYXJhdGUgdHJhamVjdG9yaWVzIHRocm91Z2ggaXRzIGNsdXN0ZXJpbmcgcHJvY2VkdXJlLiBSZWNhbGwgdGhhdCB3ZSBydW4gYGNsdXN0ZXJfY2VsbHMoKWAsIGVhY2ggY2VsbCBpcyBhc3NpZ25lZCBub3Qgb25seSB0byBhIGNsdXN0ZXIgYnV0IGFsc28gdG8gYSBwYXJ0aXRpb24uIFdoZW4geW91IGFyZSBsZWFybmluZyB0cmFqZWN0b3JpZXMsIGVhY2ggcGFydGl0aW9uIHdpbGwgZXZlbnR1YWxseSBiZWNvbWUgYSBzZXBhcmF0ZSB0cmFqZWN0b3J5LiBXZSBydW4gYGNsdXN0ZXJfY2VsbHMoKWAgYXMgYmVmb3JlLg0KDQpgYGB7ciBjbHVzdGVyLWNlbGxzfQ0KY2RzIDwtIGNsdXN0ZXJfY2VsbHMoY2RzKQ0KcDMgPC0gcGxvdF9jZWxscyhjZHMsIGNvbG9yX2NlbGxzX2J5ID0gInBhcnRpdGlvbiIpDQpnZ3NhdmUoImZpZ3VyZXMvbW9ub2NsZTNfdHJhamVjdG9yeV9wYXJ0aXRpb25zLnBkZiIsIHAzLCB3aWR0aD0xMiwgaGVpZ2h0PTEwKQ0KZ2dzYXZlKCJmaWd1cmVzL21vbm9jbGUzX3RyYWplY3RvcnlfcGFydGl0aW9ucy5qcGciLCBwMywgd2lkdGg9MTIsIGhlaWdodD0xMCkNCmBgYA0KDQo8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBjZW50ZXI7Ij4NCjxpbWcgc3JjPSJmaWd1cmVzL21vbm9jbGUzX3RyYWplY3RvcnlfcGFydGl0aW9ucy5qcGciIGFsdD0iVU1BUCBvZiBDLiBlbGVnYW5zIGVtYnJ5b25pYyBjZWxscyBjb2xvcmVkIGJ5IHBhcnRpdGlvbiIgc3R5bGU9IndpZHRoOiA4MCU7Ii8+DQo8cCBjbGFzcz0iY2FwdGlvbiI+KipGaWd1cmUgMzoqKiBVTUFQIG9mIEMuIGVsZWdhbnMgZW1icnlvbmljIGNlbGxzIGNvbG9yZWQgYnkgcGFydGl0aW9uPC9wPg0KPC9kaXY+DQoNCiMjIPCflbjvuI8gTGVhcm4gdGhlIHRyYWplY3RvcnkgZ3JhcGgNCg0KTmV4dCwgd2Ugd2lsbCBmaXQgYSBwcmluY2lwYWwgZ3JhcGggd2l0aGluIGVhY2ggcGFydGl0aW9uIHVzaW5nIHRoZSBgbGVhcm5fZ3JhcGgoKWAgZnVuY3Rpb246DQoNCmBgYHtyIGxlYXJuLWdyYXBoLCBlY2hvPVRSVUUsIHJlc3VsdHM9J2hpZGUnfQ0KY2RzIDwtIGxlYXJuX2dyYXBoKGNkcykNCmBgYA0KYGBge3IgcGxvdC1ncmFwaH0NCnA0IDwtIHBsb3RfY2VsbHMoY2RzLA0KICAgICAgICAgICBjb2xvcl9jZWxsc19ieSA9ICJjZWxsLnR5cGUiLA0KICAgICAgICAgICBsYWJlbF9ncm91cHNfYnlfY2x1c3Rlcj1GQUxTRSwNCiAgICAgICAgICAgbGFiZWxfbGVhdmVzPUZBTFNFLA0KICAgICAgICAgICBsYWJlbF9icmFuY2hfcG9pbnRzPUZBTFNFKQ0KDQpnZ3NhdmUoImZpZ3VyZXMvbW9ub2NsZTNfdHJhamVjdG9yeV9ncmFwaC5wZGYiLCBwNCwgd2lkdGg9MTIsIGhlaWdodD0xMCkNCmdnc2F2ZSgiZmlndXJlcy9tb25vY2xlM190cmFqZWN0b3J5X2dyYXBoLmpwZyIsIHA0LCB3aWR0aD0xMiwgaGVpZ2h0PTEwKQ0KDQpgYGANCg0KPGRpdiBzdHlsZT0idGV4dC1hbGlnbjogY2VudGVyOyI+DQo8aW1nIHNyYz0iZmlndXJlcy9tb25vY2xlM190cmFqZWN0b3J5X2dyYXBoLmpwZyIgYWx0PSJVTUFQIG9mIEMuIGVsZWdhbnMgZW1icnlvbmljIGNlbGxzIHdpdGggbGVhcm5lZCB0cmFqZWN0b3J5IGdyYXBoIiBzdHlsZT0id2lkdGg6IDgwJTsiLz4NCjxwIGNsYXNzPSJjYXB0aW9uIj4qKkZpZ3VyZSA0OioqIFVNQVAgb2YgQy4gZWxlZ2FucyBlbWJyeW9uaWMgY2VsbHMgd2l0aCBsZWFybmVkIHRyYWplY3RvcnkgZ3JhcGg8L3A+DQo8L2Rpdj4NCg0KDQpUaGlzIGdyYXBoIHdpbGwgYmUgdXNlZCBpbiBtYW55IGRvd25zdHJlYW0gc3RlcHMsIHN1Y2ggYXMgYnJhbmNoIGFuYWx5c2lzIGFuZCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbi4NCg0KIyMg4o+zIE9yZGVyIHRoZSBjZWxscyBpbiBwc2V1ZG90aW1lDQoNCk9uY2Ugd2UndmUgbGVhcm5lZCBhIGdyYXBoLCB3ZSBhcmUgcmVhZHkgdG8gb3JkZXIgdGhlIGNlbGxzIGFjY29yZGluZyB0byB0aGVpciBwcm9ncmVzcyB0aHJvdWdoIHRoZSBkZXZlbG9wbWVudGFsIHByb2dyYW0uIE1vbm9jbGUgbWVhc3VyZXMgdGhpcyBwcm9ncmVzcyBpbiBwc2V1ZG90aW1lLg0KDQo+ICoqV2hhdCBpcyBwc2V1ZG90aW1lPyoqDQo+DQo+IFBzZXVkb3RpbWUgaXMgYSBtZWFzdXJlIG9mIGhvdyBtdWNoIHByb2dyZXNzIGFuIGluZGl2aWR1YWwgY2VsbCBoYXMgbWFkZSB0aHJvdWdoIGEgcHJvY2VzcyBzdWNoIGFzIGNlbGwgZGlmZmVyZW50aWF0aW9uLg0KPg0KPiBJbiBtYW55IGJpb2xvZ2ljYWwgcHJvY2Vzc2VzLCBjZWxscyBkbyBub3QgcHJvZ3Jlc3MgaW4gcGVyZmVjdCBzeW5jaHJvbnkuIEluIHNpbmdsZS1jZWxsIGV4cHJlc3Npb24gc3R1ZGllcyBvZiBwcm9jZXNzZXMgc3VjaCBhcyBjZWxsIGRpZmZlcmVudGlhdGlvbiwgY2FwdHVyZWQgY2VsbHMgbWlnaHQgYmUgd2lkZWx5IGRpc3RyaWJ1dGVkIGluIHRlcm1zIG9mIHByb2dyZXNzLiBUaGF0IGlzLCBpbiBhIHBvcHVsYXRpb24gb2YgY2VsbHMgY2FwdHVyZWQgYXQgZXhhY3RseSB0aGUgc2FtZSB0aW1lLCBzb21lIGNlbGxzIG1pZ2h0IGJlIGZhciBhbG9uZywgd2hpbGUgb3RoZXJzIG1pZ2h0IG5vdCB5ZXQgZXZlbiBoYXZlIGJlZ3VuIHRoZSBwcm9jZXNzLiBUaGlzIGFzeW5jaHJvbnkgY3JlYXRlcyBtYWpvciBwcm9ibGVtcyB3aGVuIHlvdSB3YW50IHRvIHVuZGVyc3RhbmQgdGhlIHNlcXVlbmNlIG9mIHJlZ3VsYXRvcnkgY2hhbmdlcyB0aGF0IG9jY3VyIGFzIGNlbGxzIHRyYW5zaXRpb24gZnJvbSBvbmUgc3RhdGUgdG8gdGhlIG5leHQuIFRyYWNraW5nIHRoZSBleHByZXNzaW9uIGFjcm9zcyBjZWxscyBjYXB0dXJlZCBhdCB0aGUgc2FtZSB0aW1lIHByb2R1Y2VzIGEgdmVyeSBjb21wcmVzc2VkIHNlbnNlIG9mIGEgZ2VuZSdzIGtpbmV0aWNzLCBhbmQgdGhlIGFwcGFyZW50IHZhcmlhYmlsaXR5IG9mIHRoYXQgZ2VuZSdzIGV4cHJlc3Npb24gd2lsbCBiZSB2ZXJ5IGhpZ2guDQo+DQo+IEJ5IG9yZGVyaW5nIGVhY2ggY2VsbCBhY2NvcmRpbmcgdG8gaXRzIHByb2dyZXNzIGFsb25nIGEgbGVhcm5lZCB0cmFqZWN0b3J5LCBNb25vY2xlIGFsbGV2aWF0ZXMgdGhlIHByb2JsZW1zIHRoYXQgYXJpc2UgZHVlIHRvIGFzeW5jaHJvbnkuIEluc3RlYWQgb2YgdHJhY2tpbmcgY2hhbmdlcyBpbiBleHByZXNzaW9uIGFzIGEgZnVuY3Rpb24gb2YgdGltZSwgTW9ub2NsZSB0cmFja3MgY2hhbmdlcyBhcyBhIGZ1bmN0aW9uIG9mIHByb2dyZXNzIGFsb25nIHRoZSB0cmFqZWN0b3J5LCB3aGljaCB3ZSB0ZXJtICJwc2V1ZG90aW1lIi4gUHNldWRvdGltZSBpcyBhbiBhYnN0cmFjdCB1bml0IG9mIHByb2dyZXNzOiBpdCdzIHNpbXBseSB0aGUgZGlzdGFuY2UgYmV0d2VlbiBhIGNlbGwgYW5kIHRoZSBzdGFydCBvZiB0aGUgdHJhamVjdG9yeSwgbWVhc3VyZWQgYWxvbmcgdGhlIHNob3J0ZXN0IHBhdGguIFRoZSB0cmFqZWN0b3J5J3MgdG90YWwgbGVuZ3RoIGlzIGRlZmluZWQgaW4gdGVybXMgb2YgdGhlIHRvdGFsIGFtb3VudCBvZiB0cmFuc2NyaXB0aW9uYWwgY2hhbmdlIHRoYXQgYSBjZWxsIHVuZGVyZ29lcyBhcyBpdCBtb3ZlcyBmcm9tIHRoZSBzdGFydGluZyBzdGF0ZSB0byB0aGUgZW5kIHN0YXRlLg0KDQpJbiBvcmRlciB0byBwbGFjZSB0aGUgY2VsbHMgaW4gb3JkZXIsIHdlIG5lZWQgdG8gdGVsbCBNb25vY2xlIHdoZXJlIHRoZSAiYmVnaW5uaW5nIiBvZiB0aGUgYmlvbG9naWNhbCBwcm9jZXNzIGlzLiBXZSBkbyBzbyBieSBjaG9vc2luZyByZWdpb25zIG9mIHRoZSBncmFwaCB0aGF0IHdlIG1hcmsgYXMgInJvb3RzIiBvZiB0aGUgdHJhamVjdG9yeS4gSW4gdGltZSBzZXJpZXMgZXhwZXJpbWVudHMsIHRoaXMgY2FuIHVzdWFsbHkgYmUgYWNjb21wbGlzaGVkIGJ5IGZpbmRpbmcgc3BvdHMgaW4gdGhlIFVNQVAgc3BhY2UgdGhhdCBhcmUgb2NjdXBpZWQgYnkgY2VsbHMgZnJvbSBlYXJseSB0aW1lIHBvaW50czoNCg0KYGBge3IgcGxvdC10aW1lLWJpbn0NCnA1IDwtIHBsb3RfY2VsbHMoY2RzLA0KICAgICAgICAgICBjb2xvcl9jZWxsc19ieSA9ICJlbWJyeW8udGltZS5iaW4iLA0KICAgICAgICAgICBsYWJlbF9jZWxsX2dyb3Vwcz1GQUxTRSwNCiAgICAgICAgICAgbGFiZWxfbGVhdmVzPVRSVUUsDQogICAgICAgICAgIGxhYmVsX2JyYW5jaF9wb2ludHM9VFJVRSwNCiAgICAgICAgICAgZ3JhcGhfbGFiZWxfc2l6ZT0xLjUpDQpnZ3NhdmUoImZpZ3VyZXMvbW9ub2NsZTNfdHJhamVjdG9yeV90aW1lX2Jpbi5wZGYiLCBwNSwgd2lkdGg9MTIsIGhlaWdodD0xMCkNCmdnc2F2ZSgiZmlndXJlcy9tb25vY2xlM190cmFqZWN0b3J5X3RpbWVfYmluLmpwZyIsIHA1LCB3aWR0aD0xMiwgaGVpZ2h0PTEwKQ0KDQpgYGANCg0KPGRpdiBzdHlsZT0idGV4dC1hbGlnbjogY2VudGVyOyI+DQo8aW1nIHNyYz0iZmlndXJlcy9tb25vY2xlM190cmFqZWN0b3J5X3RpbWVfYmluLmpwZyIgYWx0PSJVTUFQIG9mIEMuIGVsZWdhbnMgZW1icnlvbmljIGNlbGxzIGNvbG9yZWQgYnkgZW1icnlvIHRpbWUgYmluIiBzdHlsZT0id2lkdGg6IDgwJTsiLz4NCjxwIGNsYXNzPSJjYXB0aW9uIj4qKkZpZ3VyZSA1OioqIFVNQVAgb2YgQy4gZWxlZ2FucyBlbWJyeW9uaWMgY2VsbHMgY29sb3JlZCBieSBlbWJyeW8gdGltZSBiaW48L3A+DQo8L2Rpdj4NCg0KVGhlIGJsYWNrIGxpbmVzIHNob3cgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZ3JhcGguIE5vdGUgdGhhdCB0aGUgZ3JhcGggaXMgbm90IGZ1bGx5IGNvbm5lY3RlZDogY2VsbHMgaW4gZGlmZmVyZW50IHBhcnRpdGlvbnMgYXJlIGluIGRpc3RpbmN0IGNvbXBvbmVudHMgb2YgdGhlIGdyYXBoLiBUaGUgY2lyY2xlcyB3aXRoIG51bWJlcnMgaW4gdGhlbSBkZW5vdGUgc3BlY2lhbCBwb2ludHMgd2l0aGluIHRoZSBncmFwaC4gRWFjaCBsZWFmLCBkZW5vdGVkIGJ5IGxpZ2h0IGdyYXkgY2lyY2xlcywgY29ycmVzcG9uZHMgdG8gYSBkaWZmZXJlbnQgb3V0Y29tZSAoaS5lLiBjZWxsIGZhdGUpIG9mIHRoZSB0cmFqZWN0b3J5LiBCbGFjayBjaXJjbGVzIGluZGljYXRlIGJyYW5jaCBub2RlcywgaW4gd2hpY2ggY2VsbHMgY2FuIHRyYXZlbCB0byBvbmUgb2Ygc2V2ZXJhbCBvdXRjb21lcy4gWW91IGNhbiBjb250cm9sIHdoZXRoZXIgb3Igbm90IHRoZXNlIGFyZSBzaG93biBpbiB0aGUgcGxvdCB3aXRoIHRoZSBgbGFiZWxfbGVhdmVzYCBhbmQgYGxhYmVsX2JyYW5jaF9wb2ludHNgIGFyZ3VtZW50cyB0byBgcGxvdF9jZWxsc2AuIFBsZWFzZSBub3RlIHRoYXQgbnVtYmVycyB3aXRoaW4gdGhlIGNpcmNsZXMgYXJlIHByb3ZpZGVkIGZvciByZWZlcmVuY2UgcHVycG9zZXMgb25seS4NCg0KTm93IHRoYXQgd2UgaGF2ZSBhIHNlbnNlIG9mIHdoZXJlIHRoZSBlYXJseSBjZWxscyBmYWxsLCB3ZSBjYW4gY2FsbCBgb3JkZXJfY2VsbHMoKWAsIHdoaWNoIHdpbGwgY2FsY3VsYXRlIHdoZXJlIGVhY2ggY2VsbCBmYWxscyBpbiBwc2V1ZG90aW1lLiBJbiBvcmRlciB0byBkbyBzbyBgb3JkZXJfY2VsbHMoKWAgbmVlZHMgeW91IHRvIHNwZWNpZnkgdGhlIHJvb3Qgbm9kZXMgb2YgdGhlIHRyYWplY3RvcnkgZ3JhcGguIElmIHlvdSBkb24ndCBwcm92aWRlIHRoZW0gYXMgYW4gYXJndW1lbnQsIGl0IHdpbGwgbGF1bmNoIGEgZ3JhcGhpY2FsIHVzZXIgaW50ZXJmYWNlIGZvciBzZWxlY3Rpbmcgb25lIG9yIG1vcmUgcm9vdCBub2Rlcy4NCg0KYGBge3IgZGVmaW5lLWdldC1yb290LW5vZGV9DQojIGEgaGVscGVyIGZ1bmN0aW9uIHRvIGlkZW50aWZ5IHRoZSByb290IHByaW5jaXBhbCBwb2ludHM6DQpnZXRfZWFybGllc3RfcHJpbmNpcGFsX25vZGUgPC0gZnVuY3Rpb24oY2RzLCB0aW1lX2Jpbj0iMTMwLTE3MCIpew0KICBjZWxsX2lkcyA8LSB3aGljaChjb2xEYXRhKGNkcylbLCAiZW1icnlvLnRpbWUuYmluIl0gPT0gdGltZV9iaW4pDQoNCiAgY2xvc2VzdF92ZXJ0ZXggPC0NCiAgY2RzQHByaW5jaXBhbF9ncmFwaF9hdXhbWyJVTUFQIl1dJHByX2dyYXBoX2NlbGxfcHJval9jbG9zZXN0X3ZlcnRleA0KICBjbG9zZXN0X3ZlcnRleCA8LSBhcy5tYXRyaXgoY2xvc2VzdF92ZXJ0ZXhbY29sbmFtZXMoY2RzKSwgXSkNCiAgcm9vdF9wcl9ub2RlcyA8LQ0KICBpZ3JhcGg6OlYocHJpbmNpcGFsX2dyYXBoKGNkcylbWyJVTUFQIl1dKSRuYW1lW2FzLm51bWVyaWMobmFtZXMNCiAgKHdoaWNoLm1heCh0YWJsZShjbG9zZXN0X3ZlcnRleFtjZWxsX2lkcyxdKSkpKV0NCg0KICByb290X3ByX25vZGVzDQp9DQpgYGANCg0KYGBge3Igb3JkZXItY2VsbHMtcHJvZ3JhbW1hdGljfQ0KY2RzIDwtIG9yZGVyX2NlbGxzKGNkcywgcm9vdF9wcl9ub2Rlcz1nZXRfZWFybGllc3RfcHJpbmNpcGFsX25vZGUoY2RzKSkNCmBgYA0KDQpJbiB0aGUgYWJvdmUgZXhhbXBsZSwgd2UganVzdCBjaG9zZSBvbmUgbG9jYXRpb24sIGJ1dCB5b3UgY291bGQgcGljayBhcyBtYW55IGFzIHlvdSB3YW50LiBQbG90dGluZyB0aGUgY2VsbHMgYW5kIGNvbG9yaW5nIHRoZW0gYnkgcHNldWRvdGltZSBzaG93cyBob3cgdGhleSB3ZXJlIG9yZGVyZWQ6DQoNCmBgYHtyIHBsb3QtcHNldWRvdGltZS1pbnRlcmFjdGl2ZX0NCg0KcDYgPC0gcGxvdF9jZWxscyhjZHMsDQogICAgICAgICAgIA0KICAgICAgICAgICBjb2xvcl9jZWxsc19ieSA9ICJwc2V1ZG90aW1lIiwNCiAgICAgICAgICAgbGFiZWxfY2VsbF9ncm91cHM9RkFMU0UsDQogICAgICAgICAgIGxhYmVsX2xlYXZlcz1GQUxTRSwNCiAgICAgICAgICAgbGFiZWxfYnJhbmNoX3BvaW50cz1GQUxTRSwNCiAgICAgICAgICAgZ3JhcGhfbGFiZWxfc2l6ZT0xLjUpDQoNCmdnc2F2ZSgiZmlndXJlcy9tb25vY2xlM190cmFqZWN0b3J5X3BzZXVkb3RpbWVfaW50ZXJhY3RpdmUucGRmIiwgcDYsIHdpZHRoPTEyLCBoZWlnaHQ9MTApDQpnZ3NhdmUoImZpZ3VyZXMvbW9ub2NsZTNfdHJhamVjdG9yeV9wc2V1ZG90aW1lX2ludGVyYWN0aXZlLmpwZyIsIHA2LCB3aWR0aD0xMiwgaGVpZ2h0PTEwKQ0KDQpgYGANCg0KPGRpdiBzdHlsZT0idGV4dC1hbGlnbjogY2VudGVyOyI+DQo8aW1nIHNyYz0iZmlndXJlcy9tb25vY2xlM190cmFqZWN0b3J5X3BzZXVkb3RpbWVfaW50ZXJhY3RpdmUuanBnIiBhbHQ9IlVNQVAgb2YgQy4gZWxlZ2FucyBlbWJyeW9uaWMgY2VsbHMgY29sb3JlZCBieSBwc2V1ZG90aW1lIiBzdHlsZT0id2lkdGg6IDgwJTsiLz4NCjxwIGNsYXNzPSJjYXB0aW9uIj4qKkZpZ3VyZSA2OioqIFVNQVAgb2YgQy4gZWxlZ2FucyBlbWJyeW9uaWMgY2VsbHMgY29sb3JlZCBieSBwc2V1ZG90aW1lPC9wPg0KPC9kaXY+DQogIA0KDQpOb3RlIHRoYXQgc29tZSBvZiB0aGUgY2VsbHMgYXJlIGdyYXkuIFRoaXMgbWVhbnMgdGhleSBoYXZlIGluZmluaXRlIHBzZXVkb3RpbWUsIGJlY2F1c2UgdGhleSB3ZXJlIG5vdCByZWFjaGFibGUgZnJvbSB0aGUgcm9vdCBub2RlcyB0aGF0IHdlcmUgcGlja2VkLiBJbiBnZW5lcmFsLCBhbnkgY2VsbCBvbiBhIHBhcnRpdGlvbiB0aGF0IGxhY2tzIGEgcm9vdCBub2RlIHdpbGwgYmUgYXNzaWduZWQgYW4gaW5maW5pdGUgcHNldWRvdGltZS4gSW4gZ2VuZXJhbCwgeW91IHNob3VsZCBjaG9vc2UgYXQgbGVhc3Qgb25lIHJvb3QgcGVyIHBhcnRpdGlvbi4NCg0KSXQncyBvZnRlbiBkZXNpcmFibGUgdG8gc3BlY2lmeSB0aGUgcm9vdCBvZiB0aGUgdHJhamVjdG9yeSBwcm9ncmFtbWF0aWNhbGx5LCByYXRoZXIgdGhhbiBtYW51YWxseSBwaWNraW5nIGl0LiBUaGUgZnVuY3Rpb24gYmVsb3cgZG9lcyBzbyBieSBmaXJzdCBncm91cGluZyB0aGUgY2VsbHMgYWNjb3JkaW5nIHRvIHdoaWNoIHRyYWplY3RvcnkgZ3JhcGggbm9kZSB0aGV5IGFyZSBuZWFyZXN0IHRvLiBUaGVuLCBpdCBjYWxjdWxhdGVzIHdoYXQgZnJhY3Rpb24gb2YgdGhlIGNlbGxzIGF0IGVhY2ggbm9kZSBjb21lIGZyb20gdGhlIGVhcmxpZXN0IHRpbWUgcG9pbnQuIFRoZW4gaXQgcGlja3MgdGhlIG5vZGUgdGhhdCBpcyBtb3N0IGhlYXZpbHkgb2NjdXBpZWQgYnkgZWFybHkgY2VsbHMgYW5kIHJldHVybnMgdGhhdCBhcyB0aGUgcm9vdC4NCg0KYGBge3IgZ2V0LXJvb3Qtbm9kZX0NCiMgYSBoZWxwZXIgZnVuY3Rpb24gdG8gaWRlbnRpZnkgdGhlIHJvb3QgcHJpbmNpcGFsIHBvaW50czoNCmdldF9lYXJsaWVzdF9wcmluY2lwYWxfbm9kZSA8LSBmdW5jdGlvbihjZHMsIHRpbWVfYmluPSIxMzAtMTcwIil7DQogIGNlbGxfaWRzIDwtIHdoaWNoKGNvbERhdGEoY2RzKVssICJlbWJyeW8udGltZS5iaW4iXSA9PSB0aW1lX2JpbikNCg0KICBjbG9zZXN0X3ZlcnRleCA8LQ0KICBjZHNAcHJpbmNpcGFsX2dyYXBoX2F1eFtbIlVNQVAiXV0kcHJfZ3JhcGhfY2VsbF9wcm9qX2Nsb3Nlc3RfdmVydGV4DQogIGNsb3Nlc3RfdmVydGV4IDwtIGFzLm1hdHJpeChjbG9zZXN0X3ZlcnRleFtjb2xuYW1lcyhjZHMpLCBdKQ0KICByb290X3ByX25vZGVzIDwtDQogIGlncmFwaDo6VihwcmluY2lwYWxfZ3JhcGgoY2RzKVtbIlVNQVAiXV0pJG5hbWVbYXMubnVtZXJpYyhuYW1lcw0KICAod2hpY2gubWF4KHRhYmxlKGNsb3Nlc3RfdmVydGV4W2NlbGxfaWRzLF0pKSkpXQ0KDQogIHJvb3RfcHJfbm9kZXMNCn0NCmNkcyA8LSBvcmRlcl9jZWxscyhjZHMsIHJvb3RfcHJfbm9kZXM9Z2V0X2VhcmxpZXN0X3ByaW5jaXBhbF9ub2RlKGNkcykpDQpgYGANCg0KUGFzc2luZyB0aGUgcHJvZ3JhbWF0aWNhbGx5IHNlbGVjdGVkIHJvb3Qgbm9kZSB0byBgb3JkZXJfY2VsbHMoKWAgdmlhIHRoZSBgcm9vdF9wcl9ub2Rlc2AgYXJndW1lbnQgeWllbGRzOg0KDQpgYGB7ciBwbG90LXBzZXVkb3RpbWUtcHJvZ3JhbW1hdGljfQ0KcDcgPC0gcGxvdF9jZWxscyhjZHMsDQogICAgICAgICAgIGNvbG9yX2NlbGxzX2J5ID0gInBzZXVkb3RpbWUiLA0KICAgICAgICAgICBsYWJlbF9jZWxsX2dyb3Vwcz1GQUxTRSwNCiAgICAgICAgICAgbGFiZWxfbGVhdmVzPUZBTFNFLA0KICAgICAgICAgICBsYWJlbF9icmFuY2hfcG9pbnRzPUZBTFNFLA0KICAgICAgICAgICBncmFwaF9sYWJlbF9zaXplPTEuNSkNCg0KZ2dzYXZlKCJmaWd1cmVzL21vbm9jbGUzX3RyYWplY3RvcnlfcHNldWRvdGltZV9wcm9ncmFtbWF0aWMucGRmIiwgcDcsIHdpZHRoPTEyLCBoZWlnaHQ9MTApDQpnZ3NhdmUoImZpZ3VyZXMvbW9ub2NsZTNfdHJhamVjdG9yeV9wc2V1ZG90aW1lX3Byb2dyYW1tYXRpYy5qcGciLCBwNywgd2lkdGg9MTIsIGhlaWdodD0xMCkNCg0KYGBgDQoNCjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsiPg0KPGltZyBzcmM9ImZpZ3VyZXMvbW9ub2NsZTNfdHJhamVjdG9yeV9wc2V1ZG90aW1lX3Byb2dyYW1tYXRpYy5qcGciIGFsdD0iVU1BUCBvZiBDLiBlbGVnYW5zIGVtYnJ5b25pYyBjZWxscyBjb2xvcmVkIGJ5IHBzZXVkb3RpbWUiIHN0eWxlPSJ3aWR0aDogODAlOyIvPg0KPHAgY2xhc3M9ImNhcHRpb24iPioqRmlndXJlIDc6KiogVU1BUCBvZiBDLiBlbGVnYW5zIGVtYnJ5b25pYyBjZWxscyBjb2xvcmVkIGJ5IHBzZXVkb3RpbWU8L3A+DQo8L2Rpdj4NCg0KDQpOb3RlIHRoYXQgd2UgY291bGQgZWFzaWx5IGRvIHRoaXMgb24gYSBwZXItcGFydGl0aW9uIGJhc2lzIGJ5IGZpcnN0IGdyb3VwaW5nIHRoZSBjZWxscyBieSBwYXJ0aXRpb24gdXNpbmcgdGhlIGBwYXJ0aXRpb25zKClgIGZ1bmN0aW9uLiBUaGlzIHdvdWxkIHJlc3VsdCBpbiBhbGwgY2VsbHMgYmVpbmcgYXNzaWduZWQgYSBmaW5pdGUgcHNldWRvdGltZS4NCg0KIyMg8J+MvyBTdWJzZXQgY2VsbHMgYnkgYnJhbmNoDQoNCkl0IGlzIG9mdGVuIHVzZWZ1bCB0byBzdWJzZXQgY2VsbHMgYmFzZWQgb24gdGhlaXIgYnJhbmNoIGluIHRoZSB0cmFqZWN0b3J5LiBUaGUgZnVuY3Rpb24gYGNob29zZV9ncmFwaF9zZWdtZW50c2AgYWxsb3dzIHlvdSB0byBkbyBzbyBpbnRlcmFjdGl2ZWx5Lg0KDQpgYGB7ciBzdWJzZXQtYnJhbmNoLCBldmFsPUZBTFNFfQ0KY2RzX3N1YiA8LSBjaG9vc2VfZ3JhcGhfc2VnbWVudHMoY2RzKQ0KYGBgDQoNCiMjIPCfp4ogV29ya2luZyB3aXRoIDNEIHRyYWplY3Rvcmllcw0KDQpZb3UgY2FuIGFsc28gcGVyZm9ybSB0cmFqZWN0b3J5IGFuYWx5c2lzIGluIDNELg0KDQpgYGB7ciAzZC10cmFqZWN0b3J5LCBlY2hvPVRSVUUsIHJlc3VsdHM9J2hpZGUnfQ0KY2RzXzNkIDwtIHJlZHVjZV9kaW1lbnNpb24oY2RzLCBtYXhfY29tcG9uZW50cyA9IDMpDQpjZHNfM2QgPC0gY2x1c3Rlcl9jZWxscyhjZHNfM2QpDQpjZHNfM2QgPC0gbGVhcm5fZ3JhcGgoY2RzXzNkKQ0KYGBgDQoNCmBgYHtyIG9yZGVyLTNkLWNlbGxzfQ0KY2RzXzNkIDwtIG9yZGVyX2NlbGxzKGNkc18zZCwgcm9vdF9wcl9ub2Rlcz1nZXRfZWFybGllc3RfcHJpbmNpcGFsX25vZGUoY2RzKSkNCg0KIyBHZXQgdGhlIFVNQVAgY29vcmRpbmF0ZXMgYW5kIHBhcnRpdGlvbiBpbmZvcm1hdGlvbg0KdW1hcF9jb29yZHMgPC0gcmVkdWNlZERpbXMoY2RzXzNkKSRVTUFQDQpjZWxsX3BhcnRpdGlvbnMgPC0gcGFydGl0aW9ucyhjZHNfM2QpDQoNCiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBmb3IgcGxvdHRpbmcNCnBsb3RfZGYgPC0gZGF0YS5mcmFtZSgNCiAgVU1BUF8xID0gdW1hcF9jb29yZHNbLDFdLA0KICBVTUFQXzIgPSB1bWFwX2Nvb3Jkc1ssMl0sDQogIFVNQVBfMyA9IHVtYXBfY29vcmRzWywzXSwNCiAgUGFydGl0aW9uID0gZmFjdG9yKGNlbGxfcGFydGl0aW9ucykNCikNCg0KIyBDcmVhdGUgYW5kIGRpc3BsYXkgaW50ZXJhY3RpdmUgM0QgcGxvdCB3aXRoIHBsb3RseQ0KIyBDcmVhdGUgdGhlIDNEIHBsb3QNCmZpZyA8LSBwbG90X2x5KHBsb3RfZGYsIA0KICAgICAgICAgICAgICB4ID0gflVNQVBfMSwgDQogICAgICAgICAgICAgIHkgPSB+VU1BUF8yLCANCiAgICAgICAgICAgICAgeiA9IH5VTUFQXzMsDQogICAgICAgICAgICAgIGNvbG9yID0gflBhcnRpdGlvbiwNCiAgICAgICAgICAgICAgdHlwZSA9ICJzY2F0dGVyM2QiLA0KICAgICAgICAgICAgICBtb2RlID0gIm1hcmtlcnMiLA0KICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSAzKSkgJT4lDQogIGxheW91dChzY2VuZSA9IGxpc3QoDQogICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIlVNQVBfMSIpLA0KICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJVTUFQXzIiKSwNCiAgICB6YXhpcyA9IGxpc3QodGl0bGUgPSAiVU1BUF8zIikNCiAgKSkNCg0KIyBEaXNwbGF5IHRoZSBwbG90IGRpcmVjdGx5DQpmaWcNCg0KYGBgDQoNCg0KDQoNCg0KDQojIyBSZWZlcmVuY2Vz